package solver

import (
	"fmt"
	"gosudoku/set"
	"log"
	"slices"
	"strings"
)

func fishLines(lines []House, cellCount int) (used bool) {
	for cand := 0; cand < 9; cand++ {
		verifiedLineNums := filterLineNums(lines, cand, cellCount)
		for _, lineNums := range combine(verifiedLineNums, cellCount) {
			ok, cellNums := verifyFishLine(lines, cand, lineNums, cellCount)
			if !ok {
				continue
			}
			if clearupFishLines(lines, cand, lineNums, cellNums) {
				used = true
				Logfish(lines, cand, lineNums, cellNums)
			}
		}
	}
	return
}

func filterLineNums(lines []House, cand int, cellCount int) []int {
	lineNums := make([]int, 0, 9)
	for lineNum, line := range lines {
		count := 0
		for _, cell := range line.cells {
			if cell.cands.Has(cand) {
				count++
			}
		}
		if count == 0 || count > cellCount {
			continue
		}
		lineNums = append(lineNums, lineNum)
	}
	return lineNums
}

func verifyFishLine(lines []House, cand int, lineNums []int, cellCount int) (ok bool, cellNums []int) {
	cellNumSet := set.NewSet()
	for _, lineNum := range lineNums {
		for cellNum, cell := range lines[lineNum].cells {
			if cell.cands.Has(cand) {
				cellNumSet.Add(cellNum)
			}
		}
	}
	cellNums = cellNumSet.GetList()
	ok = len(cellNums) == cellCount
	return
}

func Logfish(lines []House, cand int, lineNums []int, cellNums []int) {
	var b strings.Builder
	var firstHouse, secondHouse string
	if lines[0].kind == 0 { //row
		firstHouse = "r"
		secondHouse = "c"
	} else {
		firstHouse = "c"
		secondHouse = "r"
	}

	fmt.Fprint(&b, firstHouse)
	for _, num := range lineNums {
		fmt.Fprint(&b, num)
	}
	fmt.Fprint(&b, secondHouse)
	for _, num := range cellNums {
		fmt.Fprint(&b, num)
	}
	fmt.Fprintf(&b, " %v ", cand+1)
	switch len(lineNums) {
	case 2:
		fmt.Fprint(&b, "XWing")
	case 3:
		fmt.Fprint(&b, "Swordfish")
	case 4:
		fmt.Fprint(&b, "Jellyfish")
	}

	log.Println(b.String())
}

func clearupFishLines(lines []House, cand int, lineNums []int, cellNums []int) (used bool) {
	for lineNum, line := range lines {
		if slices.Contains(lineNums, lineNum) {
			continue
		}
		for _, cellNum := range cellNums {
			cell := line.cells[cellNum]
			if cell.solved() {
				continue
			}
			if cell.removeCand(cand) {
				used = true
			}
		}
	}
	return
}

func (g *Grid) XWing() (used bool) {
	if fishLines(g.rows[:], 2) {
		used = true
	}
	if fishLines(g.cols[:], 2) {
		used = true
	}
	return
}

func (g *Grid) Swordfish() (used bool) {
	if fishLines(g.rows[:], 3) {
		used = true
	}
	if fishLines(g.cols[:], 3) {
		used = true
	}
	return
}

func (g *Grid) Jellyfish() (used bool) {
	if fishLines(g.rows[:], 4) {
		used = true
	}
	if fishLines(g.cols[:], 4) {
		used = true
	}
	return
}
